//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.ComponentModel;
using System.Timers;
using System.Workflow.ComponentModel;
using Microsoft.Research.ScientificWorkflow;
using System;

namespace TridentBasicActivities.Activities
{
    [Name("Delay Activity")]
    [Description("Introduces a delay in workflow execution")]
    [WorkflowCategory("Utilities")]
    public class Delay : Activity
    {
        /// <summary>
        /// Manual reset event object used to synchronize communication between current thread and background timer.
        /// </summary>
        private System.Threading.ManualResetEvent manualResetEvent;

        /// <summary>
        /// Duration for which to delay the execution.
        /// </summary>
        public static DependencyProperty TimeoutDurationProperty =
            DependencyProperty.Register("TimeoutDuration", typeof(UInt64), typeof(Delay));

        /// <summary>
        /// Gets or sets the delay timeout duration.
        /// </summary>
        [RequiredInputParam]
        [Name("Timeout duration (ms)", "Timeout Duration (ms)")]
        [Description("Required timeout duration in milliseconds.")]
        public UInt64 TimeoutDuration
        {
            get
            {
                return ((UInt64)base.GetValue(Delay.TimeoutDurationProperty));
            }

            set
            {
                base.SetValue(Delay.TimeoutDurationProperty, value);
            }
        }

        /// <summary>
        /// Executes the activity. Introduces a delay in the workflow.
        /// </summary>
        /// <param name="executionContext"></param>
        /// <returns></returns>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            // Get the delay time set.
            UInt64 timeout = this.TimeoutDuration;
            if (timeout > 0)
            {
                // Start a timer that fires after the duration elapses.
                Timer timer = new Timer(timeout);
                timer.Elapsed += new ElapsedEventHandler(this.OnDelayTimerElapsed);
                timer.Start();

                // Create the manual reset event and wait on it for the timer to elaspe.
                // This will simulate a delay in execution.
                this.manualResetEvent = new System.Threading.ManualResetEvent(false);
                this.manualResetEvent.WaitOne();
            }

            return ActivityExecutionStatus.Closed;
        }

        /// <summary>
        /// Fired when the delay timeout duration elapses.
        /// </summary>
        /// <param name="sender">
        /// Background timer.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnDelayTimerElapsed(object sender, ElapsedEventArgs e)
        {
            Timer timer = sender as Timer;
            if (timer != null)
            {
                // Stop and dispose the timer.
                timer.Stop();
                timer.Dispose();
            }

            // Signal the event so that the thread which is waiting on it can resume.
            this.manualResetEvent.Set();
        }
    }
}
